﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Triggers;
using System.Threading.Tasks;

namespace NiuBeService
{
    /// <summary>
    /// 作业调度管理工具类
    /// </summary>
    public class QuartzManager
    {
        #region StdSchedulerFactory
        /// <summary>
        /// 调度工厂
        /// </summary>
        private static readonly ISchedulerFactory schedulerFactory = new StdSchedulerFactory(new NameValueCollection
        {
            /***
             * misfireThreshold是用来设置调度引擎对触发器超时的忍耐时间，简单来说misfireThreshold=60000(单位毫秒)默认值。
             * 那么它的意思说当一个触发器超时时间如果大于misfireThreshold的值 就认为这个触发器真正的超时(也叫Misfires)。
             * 如果一个触发器超时时间 小于misfireThreshold的值， 那么调度引擎则不认为触发器超时。也就是说调度引擎可以忍受这个超时的时间。
             */
            ["quartz.jobStore.misfireThreshold"] = "100"
        });
        #endregion

        #region Job
        #region GetJobAsync
        /// <summary>
        /// 获取一个任务
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <returns></returns>
        public static async Task<IJobDetail> GetJobAsync<T>() where T : IJob
        {
            var name = typeof(T).Name;
            return await GetJobAsync($"{name}_Job", $"{name}_JobGroup");
        }

        /// <summary>
        /// 获取一个任务
        /// </summary>
        /// <param name="jobName">任务名</param>
        /// <param name="jobGroupName">任务组名</param>
        /// <returns></returns>
        public static async Task<IJobDetail> GetJobAsync(string jobName, string jobGroupName)
        {
            var sched = await schedulerFactory.GetScheduler();
            return await sched.GetJobDetail(new JobKey(jobName, jobGroupName));
        }
        #endregion

        #region AddJobAsync
        /// <summary>
        /// 添加一个定时任务，注意：一个Job可以对应多个Trigger，但一个Trigger只能对应一个Job
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <param name="cron">时间设置，参考quartz说明文档</param>
        /// <returns></returns>
        public static async Task AddJobAsync<T>(string cron) where T : IJob
        {
            await AddJobAsync(typeof(T), cron);
        }

        /// <summary>
        /// 添加一个定时任务，注意：一个Job可以对应多个Trigger，但一个Trigger只能对应一个Job
        /// </summary>
        /// <param name="type">IJob实现类Type</param>
        /// <param name="cron">时间设置，参考quartz说明文档</param>
        /// <returns></returns>
        public static async Task AddJobAsync(Type type, string cron)
        {
            await AddJobAsync(type, $"{type.Name}_Job", $"{type.Name}_JobGroup", $"{type.Name}_Trigger", $"{type.Name}_TriggerGroup", cron);
        }

        /// <summary>
        /// 添加一个定时任务，注意：一个Job可以对应多个Trigger，但一个Trigger只能对应一个Job
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <param name="jobName">任务名</param>
        /// <param name="jobGroupName">任务组名</param>
        /// <param name="triggerName">触发器名</param>
        /// <param name="triggerGroupName">触发器组名</param>
        /// <param name="cron">时间设置，参考quartz说明文档</param>
        /// <returns></returns>
        public static async Task AddJobAsync<T>(string jobName, string jobGroupName, string triggerName, string triggerGroupName, string cron) where T : IJob
        {
            await AddJobAsync(typeof(T), jobName, jobGroupName, triggerName, triggerGroupName, cron);
        }

        /// <summary>
        /// 添加一个定时任务，注意：一个Job可以对应多个Trigger，但一个Trigger只能对应一个Job
        /// </summary>
        /// <param name="type">IJob实现类Type</param>
        /// <param name="jobName">任务名</param>
        /// <param name="jobGroupName">任务组名</param>
        /// <param name="triggerName">触发器名</param>
        /// <param name="triggerGroupName">触发器组名</param>
        /// <param name="cron">时间设置，参考quartz说明文档</param>
        /// <returns></returns>
        public static async Task AddJobAsync(Type type, string jobName, string jobGroupName, string triggerName, string triggerGroupName, string cron)
        {
            var sched = await schedulerFactory.GetScheduler();
            // 任务名，任务组，任务执行类
            var jobDetail = JobBuilder.Create(type).WithIdentity(jobName, jobGroupName).Build();
            // 触发器  
            var trigger = TriggerBuilder
                            .Create()
                            .WithIdentity(new TriggerKey(triggerName, triggerGroupName))
                            .WithSchedule(CronScheduleBuilder.CronSchedule(cron).WithMisfireHandlingInstructionDoNothing())//对错过的内容不再执行
                            .Build();
            // 调度容器设置JobDetail和Trigger
            await sched.ScheduleJob(jobDetail, trigger);
            // 启动  
            await sched.Start();
        }
        #endregion

        #region RemoveJobAsync
        /// <summary>
        /// 移除一个任务
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <returns></returns>
        public static async Task RemoveJobAsync<T>() where T : IJob
        {
            var name = typeof(T).Name;
            await RemoveJobAsync($"{name}_Job", $"{name}_JobGroup");
        }

        /// <summary>
        /// 移除一个任务
        /// </summary>
        /// <param name="jobName">任务名</param>
        /// <param name="jobGroupName">任务组名</param>
        /// <returns></returns>
        public static async Task RemoveJobAsync(string jobName, string jobGroupName)
        {
            var sched = await schedulerFactory.GetScheduler();
            await sched.DeleteJob(new JobKey(jobName, jobGroupName));
        }

        /// <summary>
        /// 移除多个任务
        /// </summary>
        /// <param name="jobs">任务名和任务组名字典集合</param>
        /// <returns></returns>
        public static async Task RemoveJobsAsync(Dictionary<string, string> jobs)
        {
            var sched = await schedulerFactory.GetScheduler();
            await sched.DeleteJobs(jobs.Select(o => new JobKey(o.Key, o.Value)).ToList());
        }
        #endregion

        #region UpdateJobAsync
        /// <summary>
        /// 更新一个任务的触发时间
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <param name="cron">时间设置，参考quartz说明文档</param>
        /// <returns></returns>
        public static async Task UpdateJobAsync<T>(string cron) where T : IJob
        {
            var name = typeof(T).Name;
            await UpdateJobAsync($"{name}_Job", $"{name}_JobGroup", $"{name}_Trigger", $"{name}_TriggerGroup", cron);
        }

        /// <summary>
        /// 更新一个任务的触发时间
        /// </summary>
        /// <param name="jobName">任务名</param>
        /// <param name="jobGroupName">任务组名</param>
        /// <param name="triggerName">新触发器名</param>
        /// <param name="triggerGroupName">新触发器组名</param>
        /// <param name="cron">时间设置，参考quartz说明文档</param>
        /// <returns></returns>
        public static async Task UpdateJobAsync(string jobName, string jobGroupName, string triggerName, string triggerGroupName, string cron)
        {
            var sched = await schedulerFactory.GetScheduler();
            var trigger = (await sched.GetTriggersOfJob(new JobKey(jobName, jobGroupName))).FirstOrDefault();
            if (trigger != null)
            {
                var ct = (CronTriggerImpl)trigger;
                if (!ct.CronExpressionString.Equals(cron, StringComparison.InvariantCultureIgnoreCase))
                {
                    // 触发器  
                    var triggerNew = TriggerBuilder
                                        .Create()
                                        .WithIdentity(triggerName, triggerGroupName)
                                        .WithSchedule(CronScheduleBuilder.CronSchedule(cron).WithMisfireHandlingInstructionDoNothing())
                                        .Build();
                    await sched.RescheduleJob(trigger.Key, triggerNew);
                    // 启动  
                    await sched.Start();
                }
            }
        }
        #endregion

        #region PauseJobAsync
        /// <summary>
        /// 暂停一个任务
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <returns></returns>
        public static async Task PauseJobAsync<T>() where T : IJob
        {
            var name = typeof(T).Name;
            await PauseJobAsync($"{name}_Job", $"{name}_JobGroup");
        }

        /// <summary>
        /// 暂停一个任务
        /// </summary>
        /// <param name="jobName">任务名</param>
        /// <param name="jobGroupName">任务组名</param>
        /// <returns></returns>
        public static async Task PauseJobAsync(string jobName, string jobGroupName)
        {
            var sched = await schedulerFactory.GetScheduler();
            await sched.PauseJob(new JobKey(jobName, jobGroupName));
        }
        #endregion

        #region ResumeJobAsync
        /// <summary>
        /// 恢复一个任务
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <returns></returns>
        public static async Task ResumeJobAsync<T>() where T : IJob
        {
            var name = typeof(T).Name;
            await ResumeJobAsync($"{name}_Job", $"{name}_JobGroup");
        }

        /// <summary>
        /// 恢复一个任务
        /// </summary>
        /// <param name="jobName">任务名</param>
        /// <param name="jobGroupName">任务组名</param>
        /// <returns></returns>
        public static async Task ResumeJobAsync(string jobName, string jobGroupName)
        {
            var sched = await schedulerFactory.GetScheduler();
            await sched.ResumeJob(new JobKey(jobName, jobGroupName));
        }
        #endregion

        #region ShutdownAllAsync
        /// <summary>
        /// 关闭所有任务，并清除所有资源
        /// </summary>
        /// <returns></returns>
        public static async Task ShutdownAllAsync()
        {
            var sched = await schedulerFactory.GetScheduler();
            await sched.Shutdown();
        }
        #endregion

        #region PauseAllAsync
        /// <summary>
        /// 暂停所有任务
        /// </summary>
        /// <returns></returns>
        public static async Task PauseAllAsync()
        {
            var sched = await schedulerFactory.GetScheduler();
            await sched.PauseAll();
        }
        #endregion

        #region ResumeAllAsync
        /// <summary>
        /// 恢复所有任务
        /// </summary>
        /// <returns></returns>
        public static async Task ResumeAllAsync()
        {
            var sched = await schedulerFactory.GetScheduler();
            await sched.ResumeAll();
        }
        #endregion
        #endregion

        #region Trigger
        #region GetTriggerStateAsync
        /// <summary>
        /// 获取指定触发器的状态
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <returns></returns>
        public static async Task<TriggerState> GetTriggerStateAsync<T>()
        {
            var name = typeof(T).Name;
            return await GetTriggerStateAsync($"{name}_Trigger", $"{name}_TriggerGroup");
        }

        /// <summary>
        /// 获取指定触发器的状态
        /// </summary>
        /// <param name="triggerName">触发器名</param>
        /// <param name="triggerGroupName">触发器组名</param>
        /// <returns></returns>
        public static async Task<TriggerState> GetTriggerStateAsync(string triggerName, string triggerGroupName)
        {
            var sched = await schedulerFactory.GetScheduler();
            return await sched.GetTriggerState(new TriggerKey(triggerName, triggerGroupName));
        }

        /// <summary>
        /// 获取指定任务的所有触发器状态
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <returns></returns>
        public static async Task<List<TriggerState>> GetTriggerStatesAsync<T>()
        {
            var name = typeof(T).Name;
            return await GetTriggerStatesAsync($"{name}_Job", $"{name}_JobGroup");
        }

        /// <summary>
        /// 获取指定任务的所有触发器状态
        /// </summary>
        /// <param name="jobName">任务名</param>
        /// <param name="jobGroupName">任务组名</param>
        /// <returns></returns>
        public static async Task<List<TriggerState>> GetTriggerStatesAsync(string jobName, string jobGroupName)
        {
            var list = new List<TriggerState>();
            var sched = await schedulerFactory.GetScheduler();
            var triggers = await sched.GetTriggersOfJob(new JobKey(jobName, jobGroupName));
            foreach (var trigger in triggers)
            {
                list.Add(await sched.GetTriggerState(trigger.Key));
            }
            return list;
        }
        #endregion

        #region GetTriggerAsync
        /// <summary>
        /// 获取一个触发器
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <returns></returns>
        public static async Task<ITrigger> GetTriggerAsync<T>()
        {
            var name = typeof(T).Name;
            return await GetTriggerAsync($"{name}_Trigger", $"{name}_TriggerGroup");
        }

        /// <summary>
        /// 获取一个触发器
        /// </summary>
        /// <param name="triggerName">触发器名</param>
        /// <param name="triggerGroupName">触发器组名</param>
        /// <returns></returns>
        public static async Task<ITrigger> GetTriggerAsync(string triggerName, string triggerGroupName)
        {
            var sched = await schedulerFactory.GetScheduler();
            return await sched.GetTrigger(new TriggerKey(triggerName, triggerGroupName));
        }

        /// <summary>
        /// 获取指定任务对应的所有触发器
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <returns></returns>
        public static async Task<IReadOnlyCollection<ITrigger>> GetTriggersAsync<T>()
        {
            var name = typeof(T).Name;
            return await GetTriggersAsync($"{name}_Job", $"{name}_JobGroup");
        }

        /// <summary>
        /// 获取指定任务对应的所有触发器
        /// </summary>
        /// <param name="jobName">任务名</param>
        /// <param name="jobGroupName">任务组名</param>
        /// <returns></returns>
        public static async Task<IReadOnlyCollection<ITrigger>> GetTriggersAsync(string jobName, string jobGroupName)
        {
            var sched = await schedulerFactory.GetScheduler();
            return await sched.GetTriggersOfJob(new JobKey(jobName, jobGroupName));
        }
        #endregion

        #region AddTriggerAsync
        /// <summary>
        /// 添加一个触发器
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <param name="cron">时间设置，参考quartz说明文档</param>
        /// <returns></returns>
        public static async Task AddTriggerAsync<T>(string cron) where T : IJob
        {
            await AddTriggerAsync<T>(null, null, cron);
        }

        /// <summary>
        /// 添加一个触发器
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <param name="triggerName">触发器名</param>
        /// <param name="triggerGroupName">触发器组名</param>
        /// <param name="cron">时间设置，参考quartz说明文档</param>
        /// <returns></returns>
        public static async Task AddTriggerAsync<T>(string triggerName, string triggerGroupName, string cron) where T : IJob
        {
            var name = typeof(T).Name;
            await AddTriggerAsync<T>($"{name}_Job", $"{name}_JobGroup", triggerName, triggerGroupName, cron);
        }

        /// <summary>
        /// 添加一个触发器
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <param name="jobName">任务名</param>
        /// <param name="jobGroupName">任务组名</param>
        /// <param name="triggerName">触发器名</param>
        /// <param name="triggerGroupName">触发器组名</param>
        /// <param name="cron">时间设置，参考quartz说明文档</param>
        /// <returns></returns>
        public static async Task AddTriggerAsync<T>(string jobName, string jobGroupName, string triggerName, string triggerGroupName, string cron) where T : IJob
        {
            var sched = await schedulerFactory.GetScheduler();
            var triggers = (await sched.GetTriggersOfJob(new JobKey(jobName, jobGroupName))).ToList();
            if (triggers?.Count > 0)
            {
                //删除原有job     
                await sched.DeleteJob(new JobKey(jobName, jobGroupName));
                //创建新触发器  
                var triggerBuilder = TriggerBuilder.Create();
                //判断触发器是否为空
                if (!string.IsNullOrEmpty(triggerName) && !string.IsNullOrEmpty(triggerGroupName))
                {
                    triggerBuilder = triggerBuilder.WithIdentity(triggerName, triggerGroupName);
                }
                var triggerNew = triggerBuilder.WithSchedule(CronScheduleBuilder.CronSchedule(cron).WithMisfireHandlingInstructionDoNothing()).Build();
                triggers.Add(triggerNew);
                var jobDetail = JobBuilder.Create<T>().WithIdentity(jobName, jobGroupName).Build();
                //重新创建job
                await sched.ScheduleJob(jobDetail, triggers, true);
                //启动  
                await sched.Start();
            }
        }
        #endregion

        #region RemoveTriggerAsync
        /// <summary>
        /// 移除一个触发器
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <returns></returns>
        public static async Task RemoveTriggerAsync<T>() where T : IJob
        {
            var name = typeof(T).Name;
            await RemoveTriggerAsync($"{name}_Trigger", $"{name}_TriggerGroup");
        }

        /// <summary>
        /// 移除一个触发器
        /// </summary>
        /// <param name="triggerName">触发器名</param>
        /// <param name="triggerGroupName">触发器组名</param>
        /// <returns></returns>
        public static async Task RemoveTriggerAsync(string triggerName, string triggerGroupName)
        {
            var sched = await schedulerFactory.GetScheduler();
            await sched.UnscheduleJob(new TriggerKey(triggerName, triggerGroupName));
        }

        /// <summary>
        /// 移除多个触发器
        /// </summary>
        /// <param name="triggers">触发器名和触发器组名字典集合</param>
        /// <returns></returns>
        public static async Task RemoveTriggersAsync(Dictionary<string, string> triggers)
        {
            var sched = await schedulerFactory.GetScheduler();
            await sched.UnscheduleJobs(triggers.Select(o => new TriggerKey(o.Key, o.Value)).ToList());
        }
        #endregion

        #region PauseTriggerAsync
        /// <summary>
        /// 暂停一个触发器
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <returns></returns>
        public static async Task PauseTriggerAsync<T>() where T : IJob
        {
            var name = typeof(T).Name;
            await PauseTriggerAsync($"{name}_Trigger", $"{name}_TriggerGroup");
        }

        /// <summary>
        /// 暂停一个触发器
        /// </summary>
        /// <param name="triggerName">触发器名</param>
        /// <param name="triggerGroupName">触发器组名</param>
        /// <returns></returns>
        public static async Task PauseTriggerAsync(string triggerName, string triggerGroupName)
        {
            var sched = await schedulerFactory.GetScheduler();
            await sched.PauseTrigger(new TriggerKey(triggerName, triggerGroupName));
        }
        #endregion

        #region ResumeTriggerAsync
        /// <summary>
        /// 恢复一个触发器
        /// </summary>
        /// <typeparam name="T">IJob实现类</typeparam>
        /// <returns></returns>
        public static async Task ResumeTriggerAsync<T>() where T : IJob
        {
            var name = typeof(T).Name;
            await ResumeTriggerAsync($"{name}_Trigger", $"{name}_TriggerGroup");
        }

        /// <summary>
        /// 恢复一个触发器
        /// </summary>
        /// <param name="triggerName">触发器名</param>
        /// <param name="triggerGroupName">触发器组名</param>
        /// <returns></returns>
        public static async Task ResumeTriggerAsync(string triggerName, string triggerGroupName)
        {
            var sched = await schedulerFactory.GetScheduler();
            await sched.ResumeTrigger(new TriggerKey(triggerName, triggerGroupName));
        }
        #endregion
        #endregion
    }
}
